package com.knowledgebooks.nlp;
	import java.awt.BorderLayout;
	import java.awt.Button;
	import java.awt.Checkbox;
	import java.awt.CheckboxGroup;
	import java.awt.Color;
	import java.awt.Dimension;
	import java.awt.Event;
	import java.awt.Font;
	import java.awt.Graphics;
	import java.awt.GridBagConstraints;
	import java.awt.GridBagLayout;
	import java.awt.Insets;
	import java.awt.Label;
	import java.awt.List;
	import java.awt.Panel;
	import java.awt.TextField;
	/*==============================================================================
	class      PixelGeneration [Reenu Iype]
	Content:   Represents the main drawing area where the LSystems 
		image appear

	===============================================================================*/
	class PixelGeneration extends generation {
		public TurtleFunction curpos, prevpos;		/* current position, previous position */
		public Pixel startingpos;			/* starting position */
		public Box boxdim;
		public prdction prodRules[];		/* array of prdction rules */
		public int iNumRules;			/* length of prodRules array */
		public Parameter Specs;			/* specifications on what/how to drive */
		public String currentLevel;			/* string with currently evaluated LSystems pattern */
		public StringBuffer nextLevel;		/* next level of LSystems pattern 
							   ... used when evaluating
							       pattern */
		public int iCur;			/* position in currentLevel */
		public String ignore;			/* characters to be ignored */
		public int incr;			/* size of a step when go forward */
		public int colorIndex;   			/* index into array of colors */
		public GlobalVariables globlvar;                     /* GlobalVariables */

		/*-----------------
		  items for drawing lines of
		  different width
		  -----------------*/
		public int leftWidth=0;			/* left width adjustment */
		public int righttWidth=0;              	/* right width adjustment */
		int lineWidth=1;                           /* current line width */
		int halfWidth=0;            		/* half line width, floored */
		double rotationInRadian=0;                          /* radians rotated in a 
							   single rotation */	
	                    
		/* constructor */
		public PixelGeneration() {
			/* instantiate object variables */
			this.curpos = new TurtleFunction();
			this.prevpos = new TurtleFunction();
			this.startingpos = new Pixel();
			this.boxdim = new Box();
		
					
			this.prodRules = new prdction[0];
			this.iNumRules = 0;
			this.Specs = new Parameter();
			this.currentLevel = "";
			this.nextLevel = new StringBuffer();
			this.iCur = 0;
			this.ignore = "Ff+-|;:#!";
			this.incr = 0;

		}


	        public static void DoGen(){
	        }


		/*--------------------------------------------------------------------
		Method:   proceslss

		---------------------------------------------------------------------*/
		public boolean proceslss(PrmPanel specs_pnl, Graphics g,double pnl_ht, double pnl_wd){

			boolean bOk;
		
			startingpos.horiz = 0;
			startingpos.vert = 0;
		

			bOk = GetSpecs(specs_pnl);

			if (bOk == false) {
				return(false);
			}


			bOk = drive();

			bOk = Interpret(g, pnl_ht, pnl_wd, specs_pnl.aColors);
			

			return(true);
		}


		/*------------------------------------------------------------------
		Method:   GetSpecs

		-------------------------------------------------------------------*/

		public boolean GetSpecs(PrmPanel specs_pnl){
			int iPred, iPost, iSucc;	/* separator indexes in rule */
			String sRule;			/* holds rule being parsed */
			int i;
			i=0;
			iNumRules = specs_pnl.listRules.countItems();
			this.prodRules = new prdction[iNumRules];

			if (specs_pnl.cbGrpLines.getCurrent()==specs_pnl.checkboxStrt) {
				Specs.nLnType = globlvar.line_start;
			} else if (specs_pnl.cbGrpLines.getCurrent()==specs_pnl.checkboxHermite) {
				Specs.nLnType = globlvar.line_hmit;
			} else {
				Specs.nLnType = globlvar.line_bsplne;
			}


			Specs.axiom = specs_pnl.textAxiom.getText();
			if (Specs.axiom == ""){
				return(false);
			}
			ignore = specs_pnl.textignore.getText();
			try {
				Specs.angle = Integer.parseInt(specs_pnl.textAngle.getText());
			} catch (NumberFormatException e) {

				System.out.println("Error!  Angle must be an integer.");
				Specs.angle = 4;
				return(false);
			}
			try {
				Specs.scale = Integer.parseInt(specs_pnl.textScale.getText());
			} catch (NumberFormatException e) {
				System.out.println("Error!  Scale must be an integer.");
				Specs.scale = 50;
				return(false);
			}

			try {
				Specs.depth = Integer.parseInt(specs_pnl.textDepth.getText());
			} catch (NumberFormatException e) {
				System.out.println("Error!  Depth must be an integer.");
				Specs.depth = 3;
				return(false);
			}

			try {
				Specs.starting_clr = Integer.parseInt(specs_pnl.textClr.getText());
			} catch (NumberFormatException e) {
				System.out.println("Error!  Color must be an integer.");
				Specs.starting_clr = 0;
				return(false);
			}

			if (Specs.starting_clr>globlvar.MAXCOLORS-1) {
				Specs.starting_clr = globlvar.MAXCOLORS-1;
			} else if (Specs.starting_clr <0) {
				Specs.starting_clr = 0;
			}


			for (i = 0; i < iNumRules; i++) {
				sRule = specs_pnl.listRules.getItem(i);


						
				iPred = sRule.indexOf('<');
				if (iPred != -1) {
					iPost = sRule.indexOf('>',iPred);
				} else {

					System.out.println("Error!  Missing '<' in rule.");	
					return(false);
				} 
				if (iPost != -1) {
					iSucc = sRule.indexOf("-->",iPost);
				} else {
					System.out.println("Error!  Missing '>' in rule.");
					return(false);
				}
				if (iSucc == -1) {
					System.out.println("Error!  Missing '-->' in rule.");
					return(false);			
				}
				
				
				/* put the rule into the array of rules */
				prodRules[i] = new prdction();
				
				/* left context */
				prodRules[i].lCtxt = sRule.substring(0,iPred); 
				prodRules[i].lCtxt = FixStr(prodRules[i].lCtxt);
				prodRules[i].lCtxtLen = prodRules[i].lCtxt.length();
				
				/* predecessor */
				prodRules[i].pred = sRule.substring(iPred+1,iPost);
				prodRules[i].pred = FixStr(prodRules[i].pred);
				prodRules[i].predLen = prodRules[i].pred.length();
				
				/* right context */
				prodRules[i].rCtxt = sRule.substring(iPost+1, iSucc);
				prodRules[i].rCtxt = FixStr(prodRules[i].rCtxt);
				prodRules[i].rCtxtLen = prodRules[i].rCtxt.length();
				
				/* successor */
				prodRules[i].succ = sRule.substring(iSucc+3);
				prodRules[i].succ = FixStr(prodRules[i].succ);
				prodRules[i].succLen = prodRules[i].succ.length();
			}

			return(true);
		}


		/*------------------------------------------------------------------
		Method:   FixStr

		-------------------------------------------------------------------*/
		public String FixStr(String sStr) {
			sStr = sStr.trim();
			if (sStr.equals("*")==true) {
				sStr = new String("");
			}		
			
			return(sStr);

		}

		/*--------------------------------------------------------------------

		--------------------------------------------------------------------*/
		public boolean drive(){
			int i;

			currentLevel = Specs.axiom;
			iCur = 0;
			nextLevel = new StringBuffer();
			
			/* drive each level */
			for(i=1; i<=Specs.depth;i++){
			
				iCur=0;
				/* keep going until done deriving current level string */
				while(iCur < currentLevel.length()){
					ApplyProd(FindProd());
				}
				currentLevel = new String(nextLevel);
				nextLevel = new StringBuffer();

			}

			return(true);	
		
		}


		/*-------------------------------------------------------------------
		Method:   ApplyProd
		Purpose:  replace a predecessor with its successor based on a
			  specific prdction rule

		--------------------------------------------------------------------*/
		public void ApplyProd(int RuleIdx){
			if (RuleIdx != -1){
				/* apply prdction */
				nextLevel.append(prodRules[RuleIdx].succ);	/* apply rule */
				iCur += prodRules[RuleIdx].predLen;	/* skip past current predecessor to next predecessor */
			} else {
				/* move one character down to next level (out of 
				   predecessor) */
				nextLevel.append(currentLevel.charAt(iCur));
				iCur++;   /* skip past one character */
			}
			
		}

		/*------------------------------------------------------------------
		Method:    FindProd
		
		-------------------------------------------------------------------*/
		public int FindProd(){

			int i;
			i=0;

			while(i < iNumRules){


				/* if the predecessor, left condition, and right
				   condition match then the prdction matches so 
				   return it, otherwise try the next prdction rule */

				if (prefix(prodRules[i].pred,currentLevel,iCur)&&
				    rcondiff(prodRules[i].rCtxt,currentLevel,iCur+prodRules[i].predLen)&&
				    condffl(prodRules[i].lCtxt,currentLevel,iCur-1)){
					return(i);	/* a match! */
				} else {
					i++;
				}
			
			}

			return(-1);	/* no rule found */


		}
		
		/*------------------------------------------------------------------
		Method:	  ignor
		Purpose:  determine whether the current character is one of the
			  "irrelevant" or "ignorable" characters in terms of 
			  processing an LSystems pattern

		Parms:	  sStr - string to check in
			  Idx  - index into string

		Returns:  true -- yes, should ignore the current char
			  false -- no, should not ignore the current char
		-------------------------------------------------------------------*/
		public boolean ignor (String sStr, int Idx){

			int i;

			for (i=0;i < ignore.length();i++) {
				if (sStr.charAt(Idx)==ignore.charAt(i)) {
					return(true);
				}
			}
		
			return(false);

		}


	        /*------------------------------------------------------------------
	        Method:   prefix
	        Purpose:  see if the rule string is the startinging portion found at the
	                  current position (cIdx) in the derivation string

	        Return:   true - yes, is a prefix
	                  false - no, is not a prefix

	        -------------------------------------------------------------------*/
	                
	        public boolean prefix(String sRule, String sCur, int cIdx){
	                return(sCur.regionMatches(cIdx,sRule,0,sRule.length()));                
	                
	        }

	        /*-------------------------------------------------------------------
	        Method:  rcondiff
	        Purpose:   check for a matching right context in a rule and the
	                   current position in the "derivation" string

	        Parameters:     sRule -- right context in a rule
	                        sCur  -- derivation string
	                        cIdx  -- current position in derivation string
	        
	        Returns:      true - yes, the rule and right context matched
	                      false - no match

	        --------------------------------------------------------------------*/
	        public boolean rcondiff(String sRule, String sCur, int cIdx){

	                int rIdx=0;

	                while(true) {

	                        if (rIdx >= sRule.length()) {
	                                return(true);       /* success! */
				} else if (cIdx >= sCur.length()) {
					/* we were at farthest right side of the current
					   derivation level's string (sCur).. so there is
					   no right context.. meaning it would only have
					   matched if there no right context in rule */
					return(false);	    /* no match!! */				
	                        } else if (ignor(sCur,cIdx)==true) {
					cIdx++;
				} else if (sCur.charAt(cIdx) == '[') {
	                                cIdx = this.skipright(cIdx,sCur);
	                                if (cIdx == -1) {
	                                	System.out.println("Error!  Missing ']' to terminate branch");
	                                       	return(false);   
	                                }
	                        } else if (!(sRule.charAt(rIdx) == sCur.charAt(cIdx))) {
		                                return(false);      /* no match!! */
	        	        } else {
					/* matched on this character, so try next */
					rIdx++;
					cIdx++;
				}

	                }

	        }

	        /*------------------------------------------------------------------
	        Method:  skipright
	        Purpose: skip over a branching section in a string (ie. from [ to
	                 ]) and also any subbranches contained in that branch

	        Parameters:
	                        sIdx  - index into string (assumed to be on first '['
	                        sStr  - string to search in 

	        Returns:        -1 -- error!!! didn't find closing ]
	                        other -- index into string after matching ]
	                
	        -------------------------------------------------------------------*/
	        int skipright(int sIdx, String sStr) {
	                int level = 0;
	                sIdx++;         /* get past first [ */
	                while (sIdx < sStr.length()) {
	                        switch(sStr.charAt(sIdx)) {
	                        case '[':
	                                level++;
	                                break;

	                        case ']':
	                                if (level==0) {

	                                        return(++sIdx);
	                                } else {

	                                        level--;
	                                }
	                                break;
	                        default:
	                                break;

	                        }


	                        sIdx++;
	                }

	                return((int)-1);     /* no matching ] */
	        }
	        /*-------------------------------------------------------------------
	        Method:  condffl
	        Purpose:   check for a matching left context in a rule and the
	                   current position in the "derivation" string.
				 Notice that we move through the string from left to right

	        Parameters:     sRule -- right context in a rule
	                        sCur  -- derivation string
	                        cIdx  -- current position in derivation string
	        
	        Returns:      true - yes, the rule and left context matched
	                      false - no match

	        --------------------------------------------------------------------*/
	        public boolean condffl(String sRule, String sCur, int cIdx){

	                int rIdx=sRule.length();   /* starting at end of rule */
	                rIdx--;


	                while(true) {

	                        if (rIdx < 0) {
	                                return(true);       /* success! */
				} else if(cIdx < 0) {
					/* we were at farthest left side of the current
					   derivation level's string (sCur).. so there is
					   no left context.. meaning it would only have
					   matched if there was no left context in rule */
					return(false);	    /* no match!! */	
	                        } else if (ignor(sCur,cIdx)==true){
					cIdx--;
				} else if(sCur.charAt(cIdx) == '[') {
					cIdx--;
				} else if (sCur.charAt(cIdx) == ']') {
	                                cIdx = this.skipleft(cIdx,sCur);
	                                if (cIdx == -1) {
	                                        System.out.println("Error!  Missing ']' to terminate branch");
	                                        return(false);   
	                                }
	                        } else if (!(sRule.charAt(rIdx) == sCur.charAt(cIdx))) {
	                                return(false);      /* no match!! */
	                        } else {
					/* matched on this character, so try next one */

	                		rIdx--;
			                cIdx--;
		
				}


	                }

	        }

	        /*------------------------------------------------------------------
	        Method:  skipleft
	        Purpose: skip over a branching section in a string (ie. from [ to
	                 ]) and also any subbranches contained in that branch.  Notice
			     that we go through through the string from right to left
	                
	        -------------------------------------------------------------------*/
	        int skipleft(int sIdx, String sStr) {
	                int level = 0;
	                sIdx--;         /* get past first ] */
	                while (sIdx > 0) {
	                        switch(sStr.charAt(sIdx)) {
	                        case ']':
	                                level++;
	                                break;

	                        case '[':
	                                if (level==0) {

	                                        return(--sIdx);
	                                } else {

	                                        level--;
	                                }
	                                break;
	                        default:
	                                break;

	                        }


	                        sIdx--;
	                }

	                return((int)-1);     /* no matching ] */
	        }

		/*----------------------------------------------------------------------
		Method:    Interpret
		Purpose:   interpret the meaning of the LSystems string
		

		----------------------------------------------------------------------*/

		public boolean Interpret(Graphics g, double pnl_ht, double pnl_wd, Color aColors[]) {
			incr = 1;
			/* determine points, but don't actually draw.. just find
			   dimensions of bounding box */
			draw (currentLevel,incr,Specs.angle,false,g, aColors);

			SetDrawParam(pnl_ht, pnl_wd);

			/* draw it!! */

			draw (currentLevel,incr,Specs.angle,true,g, aColors);
			
			return(true);
		}

		/*----------------------------------------------------------------------
		Method:   SetDrawParam
		Purpose:  Adjusts the step size (incr) as well as the startinging position
			  of the TurtleFunction based upon the bounding box that was required
			  for a step size of one

		------------------------------------------------------------------------*/


		public void SetDrawParam (double pnl_ht, double pnl_wd){
			
			double xscale, yscale, sc;

			/* determine how relate a "step" to the width and height
			   of the actual screen */
			xscale = (pnl_wd/(boxdim.xmax-boxdim.xmin));
			yscale = (pnl_ht/(boxdim.ymax-boxdim.ymin));

			

			/* determine whether the width or height is the tighter bound */
			if(xscale>yscale){
				sc = yscale;
			} else {
				sc = xscale;
			}


			
			incr = (int)(Math.floor((double)((sc*Specs.scale)/MAXSCALE)));
			
			startingpos.horiz = (int)((pnl_wd - (incr*(boxdim.xmin+boxdim.xmax-1)))/2);
			startingpos.vert = (int)((pnl_ht - (incr*(boxdim.ymin+boxdim.ymax-1)))/2);
			

		}

		/*----------------------------------------------------------------------
		Method:    drawWideLine
		
		Purpose:   draw a line of a specified width

		----------------------------------------------------------------------*/
		void drawWideLine(TurtleFunction startingpt, TurtleFunction endpt, Graphics g) {
			int aPolyX[] = new int[4];	/* array of X coords in polygon */
			int aPolyY[] = new int[4];	/* array of Y coords in polygon */

			TurtleFunction ll1 = new TurtleFunction();
			TurtleFunction lr1 = new TurtleFunction();
			TurtleFunction ll2 = new TurtleFunction();
			TurtleFunction lr2 = new TurtleFunction();
			TurtleFunction ll3 = new TurtleFunction();
			TurtleFunction lr3 = new TurtleFunction();
			TurtleFunction ul = new TurtleFunction();
			TurtleFunction ur = new TurtleFunction();
			double dAng;              


			if (lineWidth==1) {

				g.drawLine((int)startingpt.x, (int)startingpt.y, (int)endpt.x, (int) endpt.y);

			} else {

				/*-------------------
				 
				  -------------------*/

				ll1.x = leftWidth;
				ll1.y = 0;
				lr1.x = righttWidth;
				lr1.y = 0;
					
				dAng = -(endpt.dir*rotationInRadian); 
				ll2.x = (Math.cos(dAng)*ll1.x) -
				       (Math.sin(dAng)*ll1.y);
				ll2.y = (Math.sin(dAng)*ll1.x) +
				       (Math.cos(dAng)*ll1.y);
				ll3.x = ll2.x+startingpt.x;
				ll3.y = ll2.y+startingpt.y;	

				lr2.x = (Math.cos(dAng)*lr1.x) -
				       (Math.sin(dAng)*lr1.y);
				lr2.y = (Math.sin(dAng)*lr1.x) +
				       (Math.cos(dAng)*lr1.y);
				lr3.x = lr2.x + startingpt.x;
				lr3.y = lr2.y + startingpt.y;

					

				/*-------------------------
				  Add the points that were rotated
				  about the origin to the coordinates
				  for the endpoint of the line, giving
				  us the upper left and upper right
				  points of the polygon (ul and ur)
				  --------------------------*/	  
		
				ul.x = ll2.x+endpt.x;
				ul.y = ll2.y+endpt.y;
				
				ur.x = lr2.x+endpt.x;
				ur.y = lr2.y+endpt.y;
					

				/*------------
				  Store arrays of X and Y
				  coordinates for drawing
				  Polygons
				  ------------*/
				aPolyX[0] = (int)ll3.x;
				aPolyX[1] = (int)ul.x;
				aPolyX[2] = (int)ur.x;
				aPolyX[3] = (int)lr3.x;	

				aPolyY[0] = (int)ll3.y;
				aPolyY[1] = (int)ul.y;
				aPolyY[2] = (int)ur.y;
				aPolyY[3] = (int)lr3.y;

				g.fillPolygon(aPolyX,aPolyY,4);	
			}

		}
		

		/*-----------------------------------------------------------------------
		Method:    draw

		

			1) starting by drawing to the North

			- keep a direction specification for the TurtleFunction where 
			  	dir = 0 is North
				dir = 1 is 360/iAngFac degrees clockwise of north
					   (ie. one rotation right)
				dir = 2 is 2*(360/iAngFac) degrees clockwise of north
					   (ie. two rotations right)
				etc...
			
				so rotations right add to the dir value, 
				rotations left subtract


		-----------------------------------------------------------------------*/
		boolean draw(String sPattern, int iStepSz, int iAngFac, boolean bDoDraw, Graphics g, Color aColors[]) {

			double SI[] = new double[MAXANGLE];
			double CO[] = new double[MAXANGLE];
			TurtleFunction PosStack[] = new TurtleFunction[MAXSTACK];
			CurveTurtleFunction CrvPosStack[] = new CurveTurtleFunction[MAXSTACK];
			double dAng = -TWO_PI/4;       /* -90=270 Degrees */
			int i;
			int iMaxDir;		/* maximum direction counter value */
			int iStkIdx=-1;		/* index into stack array */
			int iMaxClrIdx=globlvar.MAXCOLORS-1;
			CurveTurtleFunction ct = new CurveTurtleFunction();
			int iHalfAngFac = iAngFac/2;         /* half of angle factor 
							        (for turning around) */



			/*----
			  Structures for storing first 3 points in current curve
			  -----*/
			TurtleFunction aFirstPts[] = new TurtleFunction[3];
			int iPtsIdx=0;		

			
			/*-------
			  Structures for creating and storing 4 vertices on a rectangle
			  for producing lines of different width 
			  -----*/


			leftWidth=0;			/* left width adjustment */
			righttWidth=0;              	/* right width adjustment */
			halfWidth = 0;        		/* half line width, floored */


			rotationInRadian = -(TWO_PI/(double)iAngFac);

			g.setColor(aColors[1]);

					
			boxdim.xmin=boxdim.xmax=curpos.x = prevpos.x = (double)(startingpos.horiz)+0.5;
			boxdim.ymin=boxdim.ymax=curpos.y = prevpos.y = (double)(startingpos.vert)+0.5;
			curpos.dir = prevpos.dir = 0;
			curpos.colorIndex = Specs.starting_clr;   
			curpos.lnWidth = lineWidth = 1;

			if (Specs.nLnType==globlvar.line_hmit) {
				ct.bDoDraw=false;
				ct.p2.getvals(curpos);
			} else if (Specs.nLnType==globlvar.line_bsplne) {
				ct.bDoDraw=true;
				ct.p1.getvals(curpos);
				ct.p2.getvals(curpos);
				ct.p3.getvals(curpos);
				ct.p4.getvals(curpos);		

				/* initialize storage of first 3 points in curve */
				aFirstPts[0] = new TurtleFunction();
				aFirstPts[1] = new TurtleFunction();
				aFirstPts[2] = new TurtleFunction();
				aFirstPts[0].getvals(curpos);
				iPtsIdx = 1;
			}



			/* precalculate the Sine/Cosine values for all of the possible
			   angle rotations that the TurtleFunction can perform */

			for (i = 0; i < iAngFac;i++) {


				SI[i] = (double)(iStepSz) * Math.sin(dAng);
				CO[i] = (double)(iStepSz) * Math.cos(dAng);


				dAng += TWO_PI/(double)iAngFac;
				
			}
		
			iMaxDir = --iAngFac;


			g.setColor(aColors[curpos.colorIndex]);
			lineWidth = curpos.lnWidth;

			/* move through string from left to right and 
			   manipulate the TurtleFunction on the screen as the 
			   pattern string specifies */

			for (i = 0; i < sPattern.length(); i++) {

				switch (sPattern.charAt(i)){

				case '|':	/* turn around (180 degrees) */
					/*---------
					  
					  ---------*/
					if (curpos.dir>=iHalfAngFac) {
						curpos.dir -= iHalfAngFac;
					} else {
						curpos.dir += iHalfAngFac;
					}
					break;

				case '#':	/* increment line width */
					curpos.lnWidth++;
					halfWidth=(int)(Math.floor(curpos.lnWidth/2));

					/*------------
					
					  ---------*/
					if ((curpos.lnWidth%2)==1) {
						leftWidth  = -halfWidth;
						righttWidth = halfWidth;
					} else {
						leftWidth  = -(halfWidth-1);
						righttWidth = halfWidth;
					} 
					lineWidth = curpos.lnWidth;


					break;

				case '!':       /* decrement line width */
					if (curpos.lnWidth>1) {
						curpos.lnWidth--;

						halfWidth=(int)(Math.floor(curpos.lnWidth/2));
		
						/*------------
						  if odd width, adjust x same both
						  left and right
				
						  if even width, adjust x one less
						  on left
						  ---------*/
						if ((curpos.lnWidth%2)==1) {
							leftWidth  = -halfWidth;
							righttWidth = halfWidth;
						} else {
							leftWidth  = -(halfWidth-1);
							righttWidth = halfWidth;
						} 

					}
					lineWidth = curpos.lnWidth;

					break;

				case ';':       /* increment color */
					if (curpos.colorIndex == iMaxClrIdx) {
						curpos.colorIndex = 0;
					} else {
						curpos.colorIndex++;
					}
					g.setColor(aColors[curpos.colorIndex]);

					break;
				case ':':	/* decrement color */
					if (curpos.colorIndex == 0) {
						curpos.colorIndex = iMaxClrIdx;
					} else {
						curpos.colorIndex--;
					}
					g.setColor(aColors[curpos.colorIndex]);

					break;	
			
				case '+': 	/* right */
					if (curpos.dir < iMaxDir) {
						curpos.dir++;
					} else {
						curpos.dir = 0;   /* did a full 360 */
					}	
					break;
				
				case '-':    	/* left */
					if (curpos.dir > 0) {
						curpos.dir--;
					} else {
						curpos.dir = iMaxDir;
					}
					break;


				case '[':	/* branching */
									
					iStkIdx++;
					if (iStkIdx < MAXSTACK) {
						/* push current position onto stack */
						PosStack[iStkIdx] = new TurtleFunction();
						PosStack[iStkIdx].getvals(curpos);  
						CrvPosStack[iStkIdx] = new CurveTurtleFunction();
						CrvPosStack[iStkIdx].getvals(ct);
						
					} else {
						System.out.println("Error!  Too many branches.");
						return(false);				
					}				
					break;
				
				case ']':	/* returning from branch */

					if (iStkIdx < 0) {
						System.out.println("Error!  Missing closing ']'");
						return(false);
					} else {
						/* pop branching position off of stack */
						curpos.getvals(PosStack[iStkIdx]);
						prevpos.getvals(curpos);
					
						if (bDoDraw==true) {
							/* draw first and last curve segments of previous 
							   curve */
							if (Specs.nLnType==globlvar.line_bsplne) {


							}
						}

						ct.getvals(CrvPosStack[iStkIdx]);
						iStkIdx--;
					}				
					lineWidth = curpos.lnWidth;
					g.setColor(aColors[curpos.colorIndex]);


					break;


				
				case 'F':	/* go forward and draw line */


					/* x' = x + cos(ang)
					   y' = y + sin(ang)
					*/

					curpos.x += CO[curpos.dir];	
					curpos.y += SI[curpos.dir];



					/* draw line or update box dimension settings */
					if (bDoDraw==true) {



						
						/*----------------------------
						  determine what type of line to draw
						  straight, Hermite curve or BSpline curve
						  -----------------------*/
							
						
							
							if (Specs.nLnType==globlvar.line_hmit) {

							if (ct.bDoDraw==true) {

								ct.p4.x = curpos.x-ct.p2.x;
								ct.p4.y = curpos.y-ct.p2.y;
								
								hermitedrawing(ct,iStepSz,g);
									
								/*ct.bDoDraw=true;*/
								ct.p2.getvals(ct.p3);
								ct.p3.getvals(curpos);
								ct.p1.getvals(ct.p4);
								
							} else {
							/*------------------------------------------
							  We have got only the starting position for 
							  drawing a segment, so we can't draw yet
							  ---------------------------------------*/
								ct.bDoDraw=true;
								ct.p3.getvals(curpos);
								ct.p1.x = curpos.x-ct.p2.x;
								ct.p1.y = curpos.y-ct.p2.y;


								
							}

							break;
							}
						
							
							if (Specs.nLnType==globlvar.line_bsplne) {
							/*------------------------
							  draw BSpline curve
							  -----------------------*/
							ct.p1.getvals(ct.p2);
							ct.p2.getvals(ct.p3);
							ct.p3.getvals(ct.p4);
							ct.p4.getvals(curpos);
							
							/*-------------
							  don't draw curve for first 3 points, just
							  store their locations (for generating
							  the first curve segment later)
							 -------------*/
							if (iPtsIdx<3) {
								aFirstPts[iPtsIdx].getvals(curpos);
								iPtsIdx++;
							} else {
								
								BSplinedrawing(ct,iStepSz,g);	
							}			
							break;

							}
							
							
							if (Specs.nLnType==globlvar.line_start) {


							drawWideLine(prevpos, curpos, g);


							break;
						}
							
						


						prevpos.getvals(curpos);  /* copy position */
											
					} else {
						UpdateBox(curpos);

					}
					

					break;


				case 'f':	/* go forward without drawing line */

					/* x' = x + cos(ang)
					   y' = y + sin(ang)
					*/


					curpos.x += CO[curpos.dir];	
					curpos.y += SI[curpos.dir];


					/* update previous position or update box dimension settings */
					if (bDoDraw==true) {
						/* draw first and last curve segments of previous 
						   curve */
						if (Specs.nLnType==globlvar.line_bsplne) {
							fstnlastdraw(ct, aFirstPts,g,iStepSz,iPtsIdx);	
						}


						if (Specs.nLnType==globlvar.line_hmit) {
							ct.bDoDraw=false;
							ct.p2.getvals(curpos);
						} else if (Specs.nLnType==globlvar.line_bsplne) {
							ct.bDoDraw=true;
							ct.p1.getvals(curpos);
							ct.p2.getvals(curpos);
							ct.p3.getvals(curpos);
							ct.p4.getvals(curpos);

							/* initialize storage of first 3 points in curve */
							aFirstPts[0].getvals(curpos);
							iPtsIdx = 1;

						}

						prevpos.getvals(curpos);  /* copy position*/
											
					} else {
						UpdateBox(curpos);

					}
					

					break;
				}						
				

			}
			if (bDoDraw==true) {

				/* draw first and last curve segments of previous 
				   curve */
				if (Specs.nLnType==globlvar.line_bsplne) {
					fstnlastdraw(ct, aFirstPts,g,iStepSz,iPtsIdx);	
				}
			}

			return(true);

		}


		/*----------------------------------------------------------------------
		Method:   fstnlastdraw
		Purpose:  Draw the first and last BSpline curve segments in a total
			  curve.
		
		Algorithm:	If the starting and end of a curve meet up then we can
				use the points at the starting and end to control the 
				curve segment.  
				
		-----------------------------------------------------------------------*/
		void fstnlastdraw(CurveTurtleFunction final_ct, TurtleFunction aFirstPts[], Graphics g, 				int iStepSz, int iPtsIdx) 
		{ 


			if ((Math.floor(final_ct.p4.x) == Math.floor(aFirstPts[0].x))&&(Math.floor(final_ct.p4.y) == Math.floor(aFirstPts[0].y))) {
			/*---------------
			  starting of curve joins with end of curve
			  --------------*/
				/* Draw end segment of curve */
				final_ct.p1.getvals(final_ct.p2);
				final_ct.p2.getvals(final_ct.p3);
				final_ct.p3.getvals(final_ct.p4);
				final_ct.p4.getvals(aFirstPts[1]);
				BSplinedrawing(final_ct,iStepSz,g);	
				
				/* Draw starting segment of curve */
				final_ct.p1 = final_ct.p2;
				final_ct.p2 = final_ct.p3;
				final_ct.p3 = final_ct.p4;
				final_ct.p4 = aFirstPts[2];
				BSplinedrawing(final_ct,iStepSz,g);	
							

			} else {
				
				
				
				


			/*--------------
			  starting of curve is independent of end of curve
			  -------------*/	
				/* Draw end segment of curve, depending on
				   last point as last TWO control points */
				final_ct.p1.getvals(final_ct.p2);
				final_ct.p2.getvals(final_ct.p3);
				final_ct.p3.getvals(final_ct.p4);
				final_ct.p4 = final_ct.p4;
				
				BSplinedrawing(final_ct,iStepSz,g);	
				
				/* only draw first curve segment if more than one
				   forward movement ('F') was encountered */
				if (iPtsIdx>2) {

					/* Draw starting segment of curve, depending on
					   first point as first TWO control points */
					final_ct.p1 = aFirstPts[0];
					final_ct.p2 = aFirstPts[0];
					final_ct.p3 = aFirstPts[1];
					final_ct.p4 = aFirstPts[2];
				BSplinedrawing(final_ct,iStepSz,g);	
				}


			}			


		}

		/*----------------------------------------------------------------------
		Method:   UpdateBox

		Purpose:  Extend the current dimensions of the box if the position
			  sent specifies an area outside of the box
		----------------------------------------------------------------------*/
		void UpdateBox(TurtleFunction boxpos) {
					
			boxdim.xmin = Math.min(boxpos.x, boxdim.xmin);
			boxdim.xmax = Math.max(boxpos.x, boxdim.xmax);
			boxdim.ymin = Math.min(boxpos.y, boxdim.ymin);
			boxdim.ymax = Math.max(boxpos.y, boxdim.ymax);
		}


		/*---------------------------------------------------------------------
		Method:   hermitedrawing
		

		----------------------------------------------------------------------*/

		void hermitedrawing(CurveTurtleFunction ct, int numParts, Graphics g){

			int i;
			double t;
			double t_inc; 
			TurtleFunction Qcur, Qprev;   /* positions on curve as drawn */


			t_inc = (double)(1/(((double)numParts-1)));

			Qcur = new TurtleFunction();
			Qprev = new TurtleFunction();

			Qprev.getvals(ct.p2);


			for (i=1,t=t_inc;i<numParts-1;i++,t+=t_inc){


				Qcur.x = ((2*Math.pow(t,3)-3*Math.pow(t,2)+1)*ct.p2.x) +
			                 ((-2*Math.pow(t,3)+3*Math.pow(t,2))*ct.p3.x) +
					 ((Math.pow(t,3)-2*Math.pow(t,2)+t)*ct.p1.x) +
					 ((Math.pow(t,3)-Math.pow(t,2))*ct.p4.x);

				Qcur.y = ((2*Math.pow(t,3)-3*Math.pow(t,2)+1)*ct.p2.y) +
			                 ((-2*Math.pow(t,3)+3*Math.pow(t,2))*ct.p3.y) +
					 ((Math.pow(t,3)-2*Math.pow(t,2)+t)*ct.p1.y) +
					 ((Math.pow(t,3)-Math.pow(t,2))*ct.p4.y);


				g.drawLine((int)Qprev.x, (int)Qprev.y, (int) Qcur.x, (int) Qcur.y);
				Qprev.getvals(Qcur);
			

					
			}			

			drawWideLine(Qprev,ct.p3,g);


		}



		/*---------------------------------------------------------------------
		Method:   BSplinedrawing

		Purpose:  Draw a BSpline curve based on 4 given control points

		----------------------------------------------------------------------*/

		void BSplinedrawing(CurveTurtleFunction ct, int numParts, Graphics g){

			int i;
			double t;
			double t_inc; 
			TurtleFunction Qcur, Qprev;   /* positions on curve as drawn */


			TurtleFunction p0 = new TurtleFunction();
			TurtleFunction p1 = new TurtleFunction();
			TurtleFunction p2 = new TurtleFunction();
			TurtleFunction p3 = new TurtleFunction();
			
			p0.getvals(ct.p1);
			p1.getvals(ct.p2);
			p2.getvals(ct.p3);
			p3.getvals(ct.p4);
			

			t_inc = (double)(1/(((double)numParts-1)));
			Qcur = new TurtleFunction();
			Qprev = new TurtleFunction();


			t=0;
			Qprev.x = ((Math.pow(1-t,3))/6*p0.x) +
					 ((3*Math.pow(t,3)-6*Math.pow(t,2)+4)/6*p1.x) +
					 ((-3*Math.pow(t,3)+3*Math.pow(t,2)+3*t+1)/6*p2.x)+
					 ((Math.pow(t,3))/6*p3.x);

			Qprev.y = ((Math.pow(1-t,3))/6*p0.y) +
					 ((3*Math.pow(t,3)-6*Math.pow(t,2)+4)/6*p1.y) +
					 ((-3*Math.pow(t,3)+3*Math.pow(t,2)+3*t+1)/6*p2.y)+
					 ((Math.pow(t,3))/6*p3.y);


			/*----------------------------------------------------
			  draw the curve, with a total of "numParts" different
			  segments drawn in the curve
			  --------------------------------------------------*/
			for (i=1,t=t_inc;i<numParts;i++,t+=t_inc){

				Qcur.x = ((Math.pow(1-t,3))/6*p0.x) +
					 ((3*Math.pow(t,3)-6*Math.pow(t,2)+4)/6*p1.x) +
					 ((-3*Math.pow(t,3)+3*Math.pow(t,2)+3*t+1)/6*p2.x)+
					 ((Math.pow(t,3))/6*p3.x);

				Qcur.y = ((Math.pow(1-t,3))/6*p0.y) +
					 ((3*Math.pow(t,3)-6*Math.pow(t,2)+4)/6*p1.y) +
					 ((-3*Math.pow(t,3)+3*Math.pow(t,2)+3*t+1)/6*p2.y)+
					 ((Math.pow(t,3))/6*p3.y);



				drawWideLine(Qprev, Qcur, g);


				Qprev.getvals(Qcur);
			

					
			}			



		}

	}

	/*========================================================================
	  Class GlobalVariables

	  This class contains GlobalVariables to be shared among all of the objects	

	  =======================================================================*/
	abstract class GlobalVariables extends Object {
		static final int MAXCOLORS = 36;      /* max number of colors in array */
	 
		static final int line_start=0;           /* line types */  
		static final int line_hmit=1;
		static final int line_bsplne=2;
		
	}
	/*=========================================================================
	Class generation

	This class is basically the common constants for the generations of
	the Lindenmayer system string
	===========================================================================*/
	abstract class generation extends Object {

	        /* constants */
	        static final int MAXPROD = 50;   /* max depth of prdctions */          
	        static final int MAXAXIOM = 100; /* max length of axiom */
	        static final int MAXSTR = 30000; /* max size of final generation string */
	        static final int MAXCHARS = 256; /* max number of ASCII character codes */
	        static final int MAXIGNORE = 50; /* max number of ignored symbols */
		static final int MAXANGLE = 40;  /* max number of rotations in 360 degrees */
		static final int MAXSCALE = 100; /* if scale is 100, full screen, 0 none*/
		static final double TWO_PI = 6.2831853;
		static final int MAXSTACK = 40;  /* max number of branches in pattern */
		static final int LEFT = 1;       /* dimensions of canvas */
		static final int RIGHT = 510;
		static final int TOP = 1;
		static final int BOTTOM = 510;

		

	}

	/*==============================================================================
	Class Parameter

	==============================================================================*/
	class Parameter extends Object {
		public String axiom;		    /* axiom */
	        public int depth;                   /* depth of derivation */
	        public int angle;                   /* angle factor (number of divisions in 360 degrees) */
	        public int scale;                 

		public int nLnType;		    /* type of line to be drawn 
								(straight, 
								 Hermite curve,
								 B-Spline) */

		public int starting_clr;		    /* startinging color */

	        /* constructor - initialize */
	        Parameter(){
	           
	                
	        }

	}

	/*==============================================================================
	Class prdction
	Contents:  Context string with string lengths
	
	==============================================================================*/
	class prdction extends Object {
	        public String lCtxt;      /* left context string */
	        public int lCtxtLen;            /* left context string length */
	        public String pred;       /* predecessor string */
	        public int predLen;             /* predecessor string length */
	        public String rCtxt;      /* right context string */
	        public int rCtxtLen;            /* right context string length */
	        public String succ;       /* successor string */
	        public int succLen;             /* successor string length */

	}

	/*==============================================================================
	Class:    Pixel
	Contents:  pixel position in the screen
	
	==============================================================================*/
	class Pixel extends Object {
		public int horiz;	/* horizontal */
		public int vert;	/* vertical */

	}

	/*==============================================================================
	Class:   Box 
	Contents: Contains attributes that defines the dimensions of the 
			bounding box for a drawing
	================================================================================*/
	class Box extends Object {
		 double xmin, xmax;
		 double ymin, ymax;
	}

